import path from 'node:path';
import fs from 'node:fs';
import { program } from 'commander';
import dayjs from 'dayjs';
import colors from 'colors/safe';

import * as Enums from '../utils/Enums';
import Printer from '../utils/Printer';
import ExecuteHelper from '../utils/ExecuteHelper';
import ModulesHelper from '../utils/ModulesHelper';
import { installNodeService, installWebApp } from './install';
import Modules from '../config/Modules';

interface BuildCommandOptions {
  type: string,
  module: string,
  env?: string,
  output?: string,
  skipInstall?: boolean,
}

export const build = async (options: BuildCommandOptions) => {
  const { type, module } = options;
  if (!type) {
    Printer.error('未指定 type 和/或 module', { type, module });
  }
  switch (type) {
    case Enums.ModuleType.App: await buildWebApp(options); break;
    case Enums.ModuleType.Service: await buildNodeService(options); break;
    default: break;
  }
};

const buildWebApp = async (options: BuildCommandOptions) => {
  const { module, env, skipInstall } = options;
  if (!module || !env) {
    Printer.error('未指定 module 和/或 env', { module, env });
    return;
  }
  const buildStartedAt = dayjs();
  Printer.log(`开始构建 ${colors.green(`${module} in ${env}`)} ...`);
  if (skipInstall) {
    await installWebApp(options);
  }
  if (Modules.isExternalApp(module)) {
    await buildExternalWebApp(options);
  } else {
    await buildInternalWenApp(options);
  }
  const buildDuration = Printer.getDurationAsSecond(buildStartedAt, dayjs());
  Printer.log(`构建成功 ${colors.green(`${module} in ${env}`)} > ${buildDuration}s`);
};

const buildInternalWenApp = async (options: BuildCommandOptions) => {
  const { module, env } = options;
  const {
    commandRunDirectory,
    webAppDirectory,
    viteConfigPath,
  } = ModulesHelper.buildWebAppExecuteParams(module);
  await ExecuteHelper.asyncRun(
    'yarn build',
    {
      cwd: commandRunDirectory,
      env: {
        NODE_ENV: 'production',
        VITE_API_ENV: env,
      },
    },
    { config: viteConfigPath },
  );
  copyDistWithOptions(webAppDirectory, options);
};

const buildExternalWebApp = async (options: BuildCommandOptions) => {
  const { module, env } = options;
  const { commandRunDirectory } = ModulesHelper.buildExternalWebAppExecuteParams(module);
  await ExecuteHelper.asyncRun(
    ModulesHelper.buildExternalWebAppYarnCommand(module, 'build'),
    {
      cwd: commandRunDirectory,
      env: {
        NODE_ENV: 'production',
        API_ENV: env,
      },
    },
  );
  copyDistWithOptions(commandRunDirectory, options);
};

const buildNodeService = async (options: BuildCommandOptions) => {
  const { module, env, skipInstall } = options;
  if (!module) {
    Printer.error('未指定 module', { module });
    return;
  }
  if (!skipInstall) {
    installNodeService(options);
  }
  if (env) {
    Printer.warn('Service 仅需要在运行时指定 env，构建时不需要指定。');
  }
  const { commandRunDirectory } = ModulesHelper.buildNodeServiceExecuteParams(module);
  await ExecuteHelper.asyncRun(
    'yarn build',
    {
      cwd: commandRunDirectory,
    },
  );
  copyDistWithOptions(commandRunDirectory, options);
};

const copyDistWithOptions = (
  webAppDirectory: string,
  options: BuildCommandOptions,
) => {
  const distDirectory = path.resolve(webAppDirectory, './dist');
  if (options.output) {
    fs.cpSync(distDirectory, options.output, { recursive: true });
  }
};

const moduleTypes = Object.values<string>(Enums.ModuleType);
const moduleTypesString = moduleTypes.map((type) => colors.green(type)).join('、');

export default program
  .command('build')
  .description('构建 app web server 或 node service')
  .option('-t, --type [type]', `类型，可取值：${moduleTypesString}`)
  .option(
    '-m, --module [name]',
    `app 或 service 的名字，如 ${colors.green('userManagement')}、${colors.green('proxy')}`,
  )
  .option('-e, --env [env]', '指定 app 连接的环境')
  .option('-o, --output [path]', '构建后输出到的目录，不指定则输出到各项目自定义的目录下')
  // TODO: 添加按 nginx 配置，构建全部 app 到指定目录的功能
  .action(build);
